﻿#region NeoPatterns
// 
// NeoPatterns framework is released under LGPLv3 license.
// See http://www.opensource.org/licenses/lgpl-3.0.html for more details.
// 
// Please visit http://neopatterns.wikidot.com for more information about NeoPatterns
// 
// Copyright (c) Pascal Craponne 2008
// 
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace NeoPatterns.Utility
{
    public static class TypeExtension
    {
        private const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

        public static PropertyInfo GetPropertyInfo(this Type type, PropertyInfo propertyInfo)
        {
            return type.GetProperty(propertyInfo.Name, bindingFlags);
        }

        public static FieldInfo GetFieldInfo(this Type type, FieldInfo fieldInfo)
        {
            return type.GetField(fieldInfo.Name, bindingFlags);
        }

        public static MethodInfo GetMethodInfo(this Type type, MethodInfo methodInfo)
        {
            var parameterTypes = (from t in methodInfo.GetParameters() select t.ParameterType).ToArray();
            return type.GetMethod(methodInfo.Name, parameterTypes);
        }

        /// <summary>
        /// Enumerates all base types
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static IEnumerable<Type> GetBaseTypes(this Type type)
        {
            for (; ; )
            {
                type = type.BaseType;
                if (type == null)
                    break;
                yield return type;
            }
        }

        /// <summary>
        /// Enumerates all base types and interfaces, 
        /// each kind (type, base types, interfaces) can be specified individually
        /// </summary>
        /// <param name="type"></param>
        /// <param name="baseType"></param>
        /// <returns></returns>
        public static IEnumerable<Type> GetBaseTypes(this Type type, BaseType baseType)
        {
            if (type == null)
                throw new NullReferenceException();
            // the first one is the base type
            if ((baseType & BaseType.Type) != 0)
                yield return type;
            // then the base types
            if ((baseType & BaseType.BaseTypes) != 0)
            {
                for (; ; )
                {
                    // a type knows its parent
                    // "root types", such as object, the parent is null
                    type = type.BaseType;
                    if (type == null)
                        break;
                    yield return type;
                }
            }
            // finally, the interfaces
            if ((baseType & BaseType.Interfaces) != 0)
            {
                foreach (var implementedInterface in type.GetInterfaces())
                    yield return implementedInterface;
            }
        }
    }
}
